package com.study.demo.aop.proxy.jvm;

import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;

import javassist.ClassPool;

import java.io.*;
import java.lang.reflect.InvocationTargetException;

/**
 * 此代理的实现过程:
 * 1.创建一个PersonProxy.class文件(跳过了PersonProxy.java)
 * 2.JVM读取这个.class文件,进行解析后, 加入JVM, 变成 Class 类
 * @author: elvin
 * @time: 2019-09-05 15:46
 * @desc:
 **/
public class Start {

    //F:\SpringStudy\aop\target\classes\com\study\demo\aop\proxy
    public static void main(String[] args) throws Exception {
        javaToByte1();
        byteToClass();
    }

    /**
     * 读取本地的class文件内的字节码，转换成字节码数组
     * @throws Exception
     */
    public static void byteToClass() throws Exception{
        //File file = new File(".");
        //InputStream input = new FileInputStream(file.getCanonicalPath() + "\\aop\\target\\classes\\com\\study\\demo\\aop\\proxy\\Person.class");
        InputStream input = new FileInputStream("C:\\Users\\Administrator\\Desktop\\aaa\\com\\study\\demo\\aop\\proxy\\PersonProxy.class");
        byte[] result = new byte[1024];

        int count = input.read(result);
        // 使用自定义的类加载器将 byte字节码数组转换为对应的class对象
        MyClassLoader loader = new MyClassLoader();
        Class clazz = loader.defineMyClass(null, result, 0, count);
        //测试加载是否成功，打印class 对象的名称
        System.out.println(clazz.getCanonicalName());

        //实例化一个Person对象
        Object o = clazz.newInstance();
        try {
            //调用Programmer的code方法
            clazz.getMethod("run", null).invoke(o, null);
        }
        catch (IllegalArgumentException | InvocationTargetException
                | NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }

    public static void javaToByte() throws Exception{

        ClassWriter classWriter = new ClassWriter(0);
        // 通过visit方法确定类的头部信息
        classWriter.visit(Opcodes.V1_8,// java版本
                Opcodes.ACC_PUBLIC,// 类修饰符
                "Person", // 类的全限定名
                null, "java/lang/Object", null);

        //创建构造函数
        MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>","()V", true);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        // 定义run方法
        MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "run", "()V",
                null, null);
        methodVisitor.visitCode();
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
                "Ljava/io/PrintStream;");
        methodVisitor.visitLdcInsn("I'm Person");
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                "(Ljava/lang/String;)V", true);
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 2);
        methodVisitor.visitEnd();
        classWriter.visitEnd();
        // 使classWriter类已经完成
        // 将classWriter转换成字节数组写到文件里面去
        byte[] data = classWriter.toByteArray();

        File file = new File("C:\\Users\\Administrator\\Desktop\\aaa\\Person.class");
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(data);
        fout.close();
    }

    public static void javaToByte1() throws Exception{
        ClassPool p = ClassPool.getDefault();
        CtClass superCt = p.getCtClass("com.study.demo.aop.proxy.Person");
        CtClass ct = p.makeClass("com.study.demo.aop.proxy.PersonProxy", superCt);
        CtMethod method = CtNewMethod.make("public void run(){}", ct);
        method.insertBefore("System.out.println(\"I'm a PersonProxy\");");
        method.insertAfter("super.run();");
        ct.addMethod(method);
        ct.writeFile("C:\\Users\\Administrator\\Desktop\\aaa");
    }
}
